//-----------------------------------------------------------------------

// <copyright file="GridLayoutStrategy.cs" company="Jamie Knight">

// Copyright (c) Jamie Knight. All rights reserved.

// </copyright>

//-----------------------------------------------------------------------

using DevKnightlie.WPF.AutoForm.FieldDefinitions;
using DevKnightlie.WPF.AutoForm.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace DevKnightlie.WPF.AutoForm
{



    /// <summary>
    /// Layout strategy which arranges input fields in a grid
    /// </summary>
    public class GridLayoutStrategy : ILayoutStrategy
    {
        #region Private fields

        private AutoFormPanel _formPanel;
        private Dictionary<BaseFieldDefinition, Control> _containerControls;
        private Dictionary<BaseFieldDefinition, Control> _fieldLabels;
        private Grid _controlsGrid;
        private int _columns = 2;
        private FieldLabelLocationType _fieldLabelLocation;
        private bool _uniformLabelWidth = true;

        #endregion


        #region properties

        /// <summary>
        /// The AutoFormPanel whose controls are being laid out
        /// </summary>
        public AutoFormPanel FormPanel
        {
            get
            {
                return _formPanel;
            }
            set
            {
                _formPanel = value;
            }
        }

        /// <summary>
        /// Sets or returns the number of fields on each row of the grid
        /// </summary>
        public int Columns
        {
            get
            {
                return _columns;
            }
            set
            {
                _columns = value;
            }
        }

        /// <summary>
        /// Used to specify where field labels should be located in relation to the input field
        /// </summary>
        public enum FieldLabelLocationType
        {
            Left,
            Above,
            Below
        };


        /// <summary>
        /// Specifies where field labels should be located in relation to input fields
        /// </summary>
        public FieldLabelLocationType FieldLabelLocation
        {
            get { return _fieldLabelLocation; }
            set { _fieldLabelLocation = value; }
        }


        /// <summary>
        /// Sets or returns a value indicating whether all field labels should be the same width
        /// </summary>
        public bool UniformLabelWidth
        {
            get { return _uniformLabelWidth; }
            set { _uniformLabelWidth = value; }
        }


        #region Events

        public delegate void WillProcessFieldDefinitionEvent(object sender, FieldDefinitionEventArgs e);

        private WillProcessFieldDefinitionEvent _willProcessFieldDefinition;

        /// <summary>
        /// Fired when the given FieldDefinition object is about to be processed
        /// </summary>
        public event WillProcessFieldDefinitionEvent WillProcessFieldDefinition
        {
            add { _willProcessFieldDefinition += value; }
            remove { _willProcessFieldDefinition -= value; }
        }


        public delegate void WillAddInputControlEvent(object sender, InputControlEventArgs e);

        private WillAddInputControlEvent _willAddInputControl;

        /// <summary>
        /// Fired when an input control is about to be added to the layout
        /// </summary>
        public event WillAddInputControlEvent WillAddInputControl
        {
            add { _willAddInputControl += value; }
            remove { _willAddInputControl -= value; }
        }


        public delegate void WillAddFieldLabelEvent(object sender, FieldLabelEventArgs e);

        private WillAddFieldLabelEvent _willAddFieldLabel;

        /// <summary>
        /// Fired when a field label is about to be added to the layout
        /// </summary>
        public event WillAddFieldLabelEvent WillAddFieldLabel
        {
            add { _willAddFieldLabel += value; }
            remove { _willAddFieldLabel -= value; }
        }

        #endregion

        #endregion

        #region Methods


        public void Initialise(Dictionary<FieldDefinitions.BaseFieldDefinition, System.Windows.Controls.Control> containerControls, Dictionary<FieldDefinitions.BaseFieldDefinition, System.Windows.Controls.Control> fieldLabels)
        {
            _containerControls = containerControls;
            _fieldLabels = fieldLabels;

            _controlsGrid = new Grid()
            {
                Visibility = System.Windows.Visibility.Visible,
                HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
                VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
                Background = System.Windows.Media.Brushes.Transparent
            };
#if DEBUG
            _controlsGrid.ShowGridLines = false;
#endif
            FormPanel.Content = _controlsGrid;
        }


        public void CreateLayout()
        {
            _controlsGrid.Children.Clear();
            int row = -1;
            int columnsRemaining = 0;

            if (_controlsGrid.ColumnDefinitions.Count < Columns)
            {
                while (_controlsGrid.ColumnDefinitions.Count < Columns)
                {
                    _controlsGrid.ColumnDefinitions.Add(new ColumnDefinition());
                }
            }
            if (_controlsGrid.ColumnDefinitions.Count > Columns)
            {
                _controlsGrid.ColumnDefinitions.RemoveRange(Columns, _controlsGrid.ColumnDefinitions.Count);
            }

            foreach (BaseFieldDefinition fieldDefinition in _containerControls.Keys)
            {
                if (_willProcessFieldDefinition != null)
                {
                    _willProcessFieldDefinition(this, new FieldDefinitionEventArgs(fieldDefinition));
                }
                // See if there are enough cells remaining to fit the control
                int columnsToUse = 0;
                switch (fieldDefinition.StretchMode)
                {
                    case BaseFieldDefinition.StretchType.None:
                        columnsToUse = 1;
                        break;
                    case BaseFieldDefinition.StretchType.Wide:
                        columnsToUse = 2;
                        break;
                    case BaseFieldDefinition.StretchType.Full:
                        columnsToUse = Columns;
                        break;
                    case BaseFieldDefinition.StretchType.Remainder:
                        columnsToUse = columnsRemaining;
                        break;
                }
                if (columnsRemaining < columnsToUse)
                {
                    row++;
                    columnsRemaining = Columns;
                }
                if (_controlsGrid.RowDefinitions.Count - 1 < row)
                {
                    _controlsGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) });
                }

                int col = Columns - columnsRemaining;
                Control containerControl = null;
                Control fieldLabel = null;
                if (_containerControls.ContainsKey(fieldDefinition))
                {
                    containerControl = _containerControls[fieldDefinition];
                }
                if (_fieldLabels.ContainsKey(fieldDefinition))
                {
                    fieldLabel = _fieldLabels[fieldDefinition];
                }
                DockPanel fieldPanel = new DockPanel()
                {
                    Margin = new System.Windows.Thickness(6)
                };
                if (fieldLabel != null)
                {
                    if (_willAddFieldLabel != null)
                    {
                        _willAddFieldLabel(this, new FieldLabelEventArgs(fieldLabel, fieldDefinition));
                    }
                    //if (fieldLabel.Parent != null)
                    //{
                    //    // TODO: Check the creation/recreation mechanism
                    //    // This terrible kludge gets around a bug in the way forms are recreated when used on a TabControl
                    //    (fieldLabel.Parent as Panel).Children.Remove(fieldLabel);
                    //}
                    fieldPanel.Children.Add(fieldLabel);
                    switch (FieldLabelLocation)
                    {
                        default:
                        case FieldLabelLocationType.Left:
                            DockPanel.SetDock(fieldLabel, Dock.Left);
                            break;
                        case FieldLabelLocationType.Above:
                            DockPanel.SetDock(fieldLabel, Dock.Top);
                            break;
                        case FieldLabelLocationType.Below:
                            DockPanel.SetDock(fieldLabel, Dock.Bottom);
                            break;
                    }
                }
                if (containerControl != null)
                {
                    if (_willAddInputControl != null)
                    {
                        _willAddInputControl(this, new InputControlEventArgs(containerControl, fieldDefinition));
                    }
                    //if (fieldDefinition.ContainerControl.Parent != null)
                    //{
                    //    // TODO: Check the creation/recreation mechanism
                    //    // This terrible kludge gets around a bug in the way forms are recreated when used on a TabControl
                    //    (fieldDefinition.ContainerControl.Parent as Panel).Children.Remove(fieldDefinition.ContainerControl);
                    //}
                    fieldPanel.Children.Add(fieldDefinition.ContainerControl);
                }
                _controlsGrid.Children.Add(fieldPanel);
                Grid.SetColumn(fieldPanel, col);
                Grid.SetRow(fieldPanel, row);
                switch (fieldDefinition.StretchMode)
                {
                    case BaseFieldDefinition.StretchType.None:
                        columnsRemaining -= fieldDefinition.Newline ? Columns : 1;
                        break;
                    case BaseFieldDefinition.StretchType.Wide:
                        fieldPanel.SetValue(Grid.ColumnSpanProperty, 2);
                        columnsRemaining -= fieldDefinition.Newline ? Columns : 2;
                        break;
                    case BaseFieldDefinition.StretchType.Full:
                        fieldPanel.SetValue(Grid.ColumnSpanProperty, Columns);
                        columnsRemaining -= Columns;
                        break;
                    default:
                        break;
                }
            }
            if (UniformLabelWidth)
            {
                double maxWidth = 0;
                foreach (Control fieldLabel in _fieldLabels.Values)
                {
                    fieldLabel.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                    if (fieldLabel.DesiredSize.Width > maxWidth)
                    {
                        maxWidth = fieldLabel.DesiredSize.Width;
                    }
                }
                if (maxWidth > 0)
                {
                    foreach (Control fieldLabel in _fieldLabels.Values)
                    {
                        fieldLabel.Width = maxWidth;
                    }
                }
            }
        }

        private void AdjustLabelWidths()
        {

        }


        public void LayoutChanged()
        {

        }



        public void DropLayout()
        {
            _controlsGrid.Children.Clear();
            _containerControls.Clear();
            _fieldLabels.Clear();
        }


        #endregion
    }
}
